<template>
  <span class="animated-counter">{{ displayValue }}</span>
</template>

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'

interface Props {
  target: number
  duration?: number
  suffix?: string
  prefix?: string
}

const props = withDefaults(defineProps<Props>(), {
  duration: 2000,
  suffix: '',
  prefix: ''
})

const displayValue = ref(props.prefix + '0' + props.suffix)
const isAnimated = ref(false)

const animateCounter = () => {
  if (isAnimated.value) return

  isAnimated.value = true
  const startTime = Date.now()
  const startValue = 0
  const endValue = props.target

  const updateCounter = () => {
    const now = Date.now()
    const progress = Math.min((now - startTime) / props.duration, 1)

    const easeOutQuart = 1 - Math.pow(1 - progress, 4)
    const currentValue = Math.floor(startValue + (endValue - startValue) * easeOutQuart)

    displayValue.value = props.prefix + currentValue + props.suffix

    if (progress < 1) {
      requestAnimationFrame(updateCounter)
    } else {
      displayValue.value = props.prefix + endValue + props.suffix
    }
  }

  requestAnimationFrame(updateCounter)
}

const observerCallback = (entries: IntersectionObserverEntry[]) => {
  entries.forEach(entry => {
    if (entry.isIntersecting && !isAnimated.value) {
      animateCounter()
    }
  })
}

onMounted(() => {
  const observer = new IntersectionObserver(observerCallback, {
    threshold: 0.5
  })

  const element = document.querySelector('.animated-counter')
  if (element) {
    observer.observe(element)
  }
})
</script>

<style scoped>
.animated-counter {
  display: inline-block;
}
</style>